/*
 * Copyright 2009 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#include <ctype.h>

#include "spdpcode.hpp"
#include "spdpsyntax.hpp"
#include "spdpname.hpp"
#include "spdpbuiltin.hpp"
#include "spdpmetainfo.hpp"

#include "spjson/spjsonport.hpp"

SP_DPCodeRender :: SP_DPCodeRender( SP_DPNameRender * nameRender )
{
	mNameRender = nameRender;
}

SP_DPCodeRender :: ~SP_DPCodeRender()
{
}

void SP_DPCodeRender :: generateHeader( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	char filename[ 128 ] = { 0 };
	mNameRender->getFileName( syntaxTree->getName(), filename, sizeof( filename ) );

	fprintf( writer, "/* %s.hpp\n", filename );
	fprintf( writer, "   Generated by spxml2struct from %s\n\n", syntaxTree->getDefineFile() );
	fprintf( writer, "   !!! DO NOT EDIT !!!\n\n" );
	fprintf( writer, "*/\n" );
	fprintf( writer, "\n" );

	fprintf( writer, "#ifndef __%s_hpp__\n", filename );
	fprintf( writer, "#define __%s_hpp__\n", filename );
	fprintf( writer, "\n" );

	fprintf( writer, "#include \"spdatapickle/spdplib.hpp\"\n" );
	fprintf( writer, "\n" );

	fprintf( writer, "#ifdef __cplusplus\n" );
	fprintf( writer, "extern \"C\" {\n" );
	fprintf( writer, "#endif\n" );
	fprintf( writer, "\n" );

	generateMetaEnum( syntaxTree, writer );

	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();

	for( ; slist->end() != sit; ++sit ) {
		generateStruct( &(*sit), writer );

		fprintf( writer, "\n" );
	}

	char name[ 128 ] = { 0 };
	mNameRender->getStructBaseName( syntaxTree->getName(), name, sizeof( name ) );

	fprintf( writer, "typedef struct tagSP_DPMetaInfo SP_DPMetaInfo_t;\n" );
	fprintf( writer, "extern SP_DPMetaInfo_t * g%sMetaInfo;\n", name );

	fprintf( writer, "\n" );

	generatePickleDefine( syntaxTree, writer );

	fprintf( writer, "\n" );
	fprintf( writer, "#ifdef __cplusplus\n" );
	fprintf( writer, "}\n" );
	fprintf( writer, "#endif\n" );

	fprintf( writer, "\n" );
	fprintf( writer, "#endif\n" );
	fprintf( writer, "\n" );
}

void SP_DPCodeRender :: generateStruct( SP_DPSyntaxStruct * structure, FILE * writer )
{
	char structName[ 128 ] = { 0 };

	mNameRender->getStructBaseName( structure->getName(), structName, sizeof( structName ) );

	fprintf( writer, "typedef struct tag%s {\n", structName );

	SP_DPSyntaxFieldVector * flist = structure->getFieldList();

	SP_DPSyntaxFieldVector::iterator fit = flist->begin();

	for( ; flist->end() != fit; ++fit ) {
		generateField( &(*fit), writer );

		if( '\0' != *(fit->getReferTo()) && flist->end() != ( fit + 1 ) ) {
			fprintf( writer, "\n" );
		}
	}

	fprintf( writer, "} %s_t;\n", structName );
}

void SP_DPCodeRender :: generateField( SP_DPSyntaxField * field, FILE * writer )
{
	char typeName[ 128 ] = { 0 }, fieldName[ 128 ] = { 0 };

	if( field->getArraySize() > 0 ) {
		fprintf( writer, "\t%s %s[%d];\n", mNameRender->getTypeName( field->getType(), typeName, sizeof( typeName ) ),
				mNameRender->getFieldName( field->getName(), fieldName, sizeof( fieldName ) ), field->getArraySize() );
	} else {
		fprintf( writer, "\t%s %s;\n", mNameRender->getTypeName( field->getType(), typeName, sizeof( typeName ) ),
				mNameRender->getFieldName( field->getName(), fieldName, sizeof( fieldName ) ) );
	}
}

void SP_DPCodeRender :: generateCpp( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	char filename[ 128 ] = { 0 };
	mNameRender->getFileName( syntaxTree->getName(), filename, sizeof( filename ) );

	fprintf( writer, "/* %s.cpp\n", filename );
	fprintf( writer, "   Generated by spxml2struct from %s\n\n", syntaxTree->getDefineFile() );
	fprintf( writer, "   !!! DO NOT EDIT !!!\n\n" );
	fprintf( writer, "*/\n" );
	fprintf( writer, "\n" );

	fprintf( writer, "#include <stdio.h>\n" );
	fprintf( writer, "\n" );
	fprintf( writer, "#include \"%s.hpp\"\n", filename );
	fprintf( writer, "\n" );
	fprintf( writer, "#include \"spdatapickle/spdplib.hpp\"\n" );
	fprintf( writer, "\n" );

	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();
	for( ; slist->end() != sit; ++sit ) {
		generateMetaField( &(*sit), writer );
	}

	generateMetaInfo( syntaxTree, writer );

	generatePickleImpl( syntaxTree, writer );
}

void SP_DPCodeRender :: generateMetaEnum( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();

	fprintf( writer, "enum {\n" );

	for( ; slist->end() != sit; ++sit ) {
		char name[ 128 ] = { 0 }, tmp[ 128 ] = { 0 };
		snprintf( name, sizeof( name ), "eType%s",
				mNameRender->getStructBaseName( sit->getName(), tmp, sizeof( tmp ) ) );

		if( slist->begin() == sit ) {
			if( syntaxTree->isBuiltin() ) {
				fprintf( writer, "\t%s = eTypeSPDPBuiltin + 1", name );
			} else {
				fprintf( writer, "\t%s = eTypeSPDPUserDefine + 1", name );
			}
		} else {
			fprintf( writer, "\t%s", name );
		}

		if( slist->end() == ( sit + 1 ) ) {
			fprintf( writer, "\n" );
		} else {
			fprintf( writer, ",\n" );
		}
	}

	fprintf( writer, "};\n" );
	fprintf( writer, "\n" );
}

void SP_DPCodeRender :: generateMetaField( SP_DPSyntaxStruct * structure, FILE * writer )
{
	char sname[ 128 ] = { 0 }, fname[ 128 ] = { 0 }, ename[ 128 ] = { 0 };
	char tname[ 128 ] = { 0 }, rawname[ 128 ] = { 0 };

	fprintf( writer, "static SP_DPMetaField_t gMeta%s [] = {\n",
			mNameRender->getStructBaseName( structure->getName(), sname, sizeof( sname ) ) );

	SP_DPSyntaxFieldVector * flist = structure->getFieldList();

	SP_DPSyntaxFieldVector::iterator fit = flist->begin();

	for( ; flist->end() != fit; ++fit ) {
		mNameRender->getFieldName( fit->getName(), fname, sizeof( fname ) );
		mNameRender->getTypeEnum( fit->getType(), ename, sizeof( ename ) );

		mNameRender->getTypeFullName( &(*fit), tname, sizeof( tname ) );
		mNameRender->getTypeRawName( fit->getType(), rawname, sizeof( rawname ) );

		int isPtr = fit->isPtr();
		if( 0 == isPtr ) isPtr = fit->getArraySize() > 0;

		fprintf( writer, "\t{ sizeof(SP_DPMetaField_t), %d, \"%s\",\n"
				"\t\tSP_DP_FIELD_OFFSET(%s_t, %s), %s, %d, %d, %d,\n"
				"\t\t\"%s\", sizeof(%s), sizeof(%s), %d }",
				fit->getId(), fit->getName(), sname, fname, ename, isPtr ? 1 : 0, fit->isRequired() ? 1 : 0,
				fit->getArraySize(), fit->getReferTo(), tname, rawname, fit->isReferred() ? 1 : 0 );

		if( flist->end() == ( fit + 1 ) ) {
			fprintf( writer, "\n" );
		} else {
			fprintf( writer, ",\n" );
		}
	}

	fprintf( writer, "};\n" );

	fprintf( writer, "\n" );
}

void SP_DPCodeRender :: generateMetaInfo( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();

	fprintf( writer, "static SP_DPMetaStruct_t gMeta%sStructList [] = {\n",
			syntaxTree->getPrefix() );

	for( ; slist->end() != sit; ++sit ) {
		char ename[ 129 ] = { 0 }, sname[ 128 ] = { 0 };
		mNameRender->getTypeEnum( sit->getName(), ename, sizeof( ename ) );
		mNameRender->getStructBaseName( sit->getName(), sname, sizeof( sname ) );

		fprintf( writer, "\t{ sizeof( SP_DPMetaStruct_t ), %s, \"%s\", sizeof(%s_t),\n"
				"\t\tSP_DP_ARRAY_SIZE(gMeta%s), gMeta%s }",
				ename, sit->getName(), sname, sname, sname );

		fprintf( writer, ",\n" );
	}

	if( 0 == syntaxTree->isBuiltin() ) {
		for( int i = 0; i < gSP_DPBuiltinMetaInfo->mStructCount; i++ ) {
				fprintf( writer, "\tgSP_DPBuiltinMetaInfo->mStructList[%d]", i );
				fprintf( writer, "%s", ( gSP_DPBuiltinMetaInfo->mStructCount - 1 ) == i ? "\n" : ",\n" );
		}
	}

	fprintf( writer, "};\n" );
	fprintf( writer, "\n" );

	char name[ 128 ] = { 0 };
	mNameRender->getStructBaseName( syntaxTree->getName(), name, sizeof( name ) );

	const char * prefix = syntaxTree->getPrefix();

	fprintf( writer, "static SP_DPMetaInfo_t gMeta%s = {\n", name );
	fprintf( writer, "\tsizeof( SP_DPMetaInfo_t ), \"%s\", \"%s\", "
			"SP_DP_ARRAY_SIZE(gMeta%sStructList), gMeta%sStructList\n",
			prefix, syntaxTree->getName(), prefix, prefix );
	fprintf( writer, "};\n" );

	fprintf( writer, "\n" );

	fprintf( writer, "SP_DPMetaInfo_t * g%sMetaInfo = &gMeta%s;\n", name, name );

	fprintf( writer, "\n" );
}

void SP_DPCodeRender :: generatePickleDefine( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	char pickleName[ 128 ] = { 0 };
	mNameRender->getPickleName( syntaxTree->getName(), pickleName, sizeof( pickleName ) );

	fprintf( writer, "class SP_XmlStringBuffer;\n" );
	fprintf( writer, "class SP_DataPickle;\n" );
	fprintf( writer, "\n" );

	fprintf( writer, "class %s {\n", pickleName );
	fprintf( writer, "public:\n" );
	fprintf( writer, "\t%s( int pickleType );\n", pickleName );
	fprintf( writer, "\t~%s();\n", pickleName );
	fprintf( writer, "\n" );

	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();

	for( ; slist->end() != sit; ++sit ) {
		char structName[ 128 ] = { 0 };
		mNameRender->getStructBaseName( sit->getName(), structName, sizeof( structName ) );

		fprintf( writer, "\tint pickle( %s_t * structure, SP_XmlStringBuffer * buffer );\n", structName );
		fprintf( writer, "\tint unpickle( SP_XmlStringBuffer * buffer, %s_t * structure );\n", structName );
		fprintf( writer, "\n" );
	}

	fprintf( writer, "\n" );

	fprintf( writer, "public:\n" );

	sit = slist->begin();

	for( ; slist->end() != sit; ++sit ) {
		char structName[ 128 ] = { 0 };
		mNameRender->getStructBaseName( sit->getName(), structName, sizeof( structName ) );

		fprintf( writer, "\tstatic int freeFields( %s_t & structure );\n", structName );
		fprintf( writer, "\tstatic int deepCopy( const %s_t * src, %s_t * dest );\n",
				structName, structName );
		fprintf( writer, "\n" );
	}

	fprintf( writer, "\n" );

	fprintf( writer, "private:\n" );
	fprintf( writer, "\tSP_DataPickle * mImpl;\n" );
	fprintf( writer, "};\n" );
	fprintf( writer, "\n" );
}

void SP_DPCodeRender :: generatePickleImpl( SP_DPSyntaxTree * syntaxTree, FILE * writer )
{
	char pickleName[ 128 ] = { 0 };
	mNameRender->getPickleName( syntaxTree->getName(), pickleName, sizeof( pickleName ) );

	char tmp[ 128 ] = { 0 }, metaName[ 128 ] = { 0 };
	mNameRender->getStructBaseName( syntaxTree->getName(), tmp, sizeof( tmp ) );

	snprintf( metaName, sizeof( metaName ), "g%sMetaInfo", tmp );

	fprintf( writer, "%s :: %s( int pickleType )\n", pickleName, pickleName );
	fprintf( writer, "{\n" );
	fprintf( writer, "\tmImpl = NULL;\n" );
	fprintf( writer, "\tif( SP_DataPickle::eXml == pickleType ) {\n" );
	fprintf( writer, "\t\tmImpl = new SP_XmlPickle( %s );\n", metaName );
	fprintf( writer, "\t} else if( SP_DataPickle::eXmlRpc == pickleType ) {\n" );
	fprintf( writer, "\t\tmImpl = new SP_XmlRpcPickle( %s );\n", metaName );
	fprintf( writer, "\t} else if( SP_DataPickle::eJson == pickleType ) {\n" );
	fprintf( writer, "\t\tmImpl = new SP_JsonPickle( %s );\n", metaName );
	fprintf( writer, "\t} else if( SP_DataPickle::eProtoBuf == pickleType ) {\n" );
	fprintf( writer, "\t\tmImpl = new SP_ProtoBufPickle( %s );\n", metaName );
	fprintf( writer, "\t}\n" );
	fprintf( writer, "}\n" );

	fprintf( writer, "%s :: ~%s()\n", pickleName, pickleName );
	fprintf( writer, "{\n" );
	fprintf( writer, "\tif( NULL != mImpl ) delete mImpl, mImpl = NULL;\n" );
	fprintf( writer, "}\n" );
	fprintf( writer, "\n" );

	SP_DPSyntaxStructVector * slist = syntaxTree->getStructList();

	SP_DPSyntaxStructVector::iterator sit = slist->begin();

	for( ; slist->end() != sit; ++sit ) {
		char structName[ 128 ] = { 0 }, typeEnum[ 128 ] = { 0 };
		mNameRender->getStructBaseName( sit->getName(), structName, sizeof( structName ) );
		mNameRender->getTypeEnum( sit->getName(), typeEnum, sizeof( typeEnum ) );

		fprintf( writer, "int %s :: pickle( %s_t * structure, SP_XmlStringBuffer * buffer )\n",
				pickleName, structName );
		fprintf( writer, "{\n" );
		fprintf( writer, "\tif( NULL == mImpl ) return -1;\n" );
		fprintf( writer, "\treturn mImpl->pickle( structure, sizeof( %s_t ),\n"
				"\t\t%s, buffer );\n", structName, typeEnum );
		fprintf( writer, "}\n" );
		fprintf( writer, "\n" );
		fprintf( writer, "int %s :: unpickle( SP_XmlStringBuffer * buffer, %s_t * structure )\n",
				pickleName, structName );
		fprintf( writer, "{\n" );
		fprintf( writer, "\tif( NULL == mImpl ) return -1;\n" );
		fprintf( writer, "\treturn mImpl->unpickle( buffer->getBuffer(), buffer->getSize(),\n"
				"\t\t%s, structure, sizeof( %s_t ) );\n",
				typeEnum, structName );
		fprintf( writer, "}\n" );
		fprintf( writer, "\n" );
	}

	fprintf( writer, "\n" );

	sit = slist->begin();

	for( ; slist->end() != sit; ++sit ) {
		char structName[ 128 ] = { 0 }, typeEnum[ 128 ] = { 0 };
		mNameRender->getStructBaseName( sit->getName(), structName, sizeof( structName ) );
		mNameRender->getTypeEnum( sit->getName(), typeEnum, sizeof( typeEnum ) );

		fprintf( writer, "int %s :: freeFields( %s_t & structure )\n", pickleName, structName );
		fprintf( writer, "{\n" );
		fprintf( writer, "\tSP_DPAlloc alloc( %s );\n", metaName  );
		fprintf( writer, "\treturn alloc.free( &structure, sizeof( structure ), %s );\n", typeEnum );
		fprintf( writer, "}\n" );
		fprintf( writer, "\n" );
		fprintf( writer, "int %s :: deepCopy( const %s_t * src, %s_t * dest )\n",
				pickleName, structName, structName );
		fprintf( writer, "{\n" );
		fprintf( writer, "\tSP_DPAlloc alloc( %s );\n", metaName  );
		fprintf( writer, "\treturn alloc.deepCopy( src, sizeof( *src ), %s, dest, sizeof( *dest ) );\n", typeEnum );
		fprintf( writer, "}\n" );
		fprintf( writer, "\n" );
	}

	fprintf( writer, "\n" );
}

